{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0d0e279f",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain-academy/blob/main/module-4/parallelization.ipynb) [![Open in LangChain Academy](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66e9eba12c7b7688aa3dbb5e_LCA-badge-green.svg)](https://academy.langchain.com/courses/take/intro-to-langgraph/lessons/58239934-lesson-1-parallelization)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4169bfb-769a-4db3-833e-c827f19024b2",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "# Parallel node execution\n",
    "\n",
    "## Review\n",
    "\n",
    "In module 3, we went in-depth on `human-in-the loop`, showing 3 common use-cases:\n",
    "\n",
    "(1) `Approval` - We can interrupt our agent, surface state to a user, and allow the user to accept an action\n",
    "\n",
    "(2) `Debugging` - We can rewind the graph to reproduce or avoid issues\n",
    "\n",
    "(3) `Editing` - You can modify the state \n",
    "\n",
    "## Goals\n",
    "\n",
    "This module will build on `human-in-the-loop` as well as the `memory` concepts discussed in module 2.\n",
    "\n",
    "We will dive into `multi-agent` workflows and build up to a multi-agent research assistant that ties together all of the modules from this course.\n",
    "\n",
    "To build this multi-agent research assistant, we'll first discuss a few LangGraph controllability topics.\n",
    "\n",
    "We'll start with [parallelization](https://docs.langchain.com/oss/python/langgraph/how-tos/graph-api#create-branches).\n",
    "\n",
    "## Fan out and fan in\n",
    "\n",
    "Let's build a simple linear graph that over-writes the state at each step."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "618eab5c-4ef7-4273-8e0b-a9c847897ed7",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install -U  langgraph langgraph_tavily wikipedia langchain_openai langchain_community langgraph_sdk"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "31bbec0d",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "import os, getpass\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "1dd77093-1794-4bd7-8c57-58f59a74c20b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "from typing import Any, List\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "class State(TypedDict):\n",
    "    # Note, no reducer function. \n",
    "    state: List[str]\n",
    "\n",
    "class ReturnNodeValue:\n",
    "    def __init__(self, node_secret: str):\n",
    "        self._value = node_secret\n",
    "\n",
    "    def __call__(self, state: State) -> Any:\n",
    "        print(f\"Adding {self._value} to {state['state']}\")\n",
    "        return {\"state\": [self._value]}\n",
    "\n",
    "# Add nodes\n",
    "builder = StateGraph(State)\n",
    "\n",
    "# Initialize each node with node_secret \n",
    "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n",
    "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n",
    "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n",
    "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n",
    "\n",
    "# Flow\n",
    "builder.add_edge(START, \"a\")\n",
    "builder.add_edge(\"a\", \"b\")\n",
    "builder.add_edge(\"b\", \"c\")\n",
    "builder.add_edge(\"c\", \"d\")\n",
    "builder.add_edge(\"d\", END)\n",
    "graph = builder.compile()\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bdd027d3-ef9f-4d43-b190-e9f07d521e18",
   "metadata": {},
   "source": [
    "We over-write state, as expected."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "bf260088-90d5-45b2-93ab-42f241560840",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding I'm A to []\n",
      "Adding I'm B to [\"I'm A\"]\n",
      "Adding I'm C to [\"I'm B\"]\n",
      "Adding I'm D to [\"I'm C\"]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'state': [\"I'm D\"]}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke({\"state\": []})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a9dec27d-dc43-4088-beb2-53ad090d2971",
   "metadata": {},
   "source": [
    "Now, let's run `b` and `c` in parallel. \n",
    "\n",
    "And then run `d`.\n",
    "\n",
    "We can do this easily with fan-out from `a` to `b` and `c`, and then fan-in to `d`.\n",
    "\n",
    "The the state updates are applied at the end of each step.\n",
    "\n",
    "Let's run it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "8fdeaaab-a8c3-470f-89ef-9cf0a2760667",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "builder = StateGraph(State)\n",
    "\n",
    "# Initialize each node with node_secret \n",
    "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n",
    "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n",
    "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n",
    "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n",
    "\n",
    "# Flow\n",
    "builder.add_edge(START, \"a\")\n",
    "builder.add_edge(\"a\", \"b\")\n",
    "builder.add_edge(\"a\", \"c\")\n",
    "builder.add_edge(\"b\", \"d\")\n",
    "builder.add_edge(\"c\", \"d\")\n",
    "builder.add_edge(\"d\", END)\n",
    "graph = builder.compile()\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35238fde-0230-4ae8-9200-158a8835c4f1",
   "metadata": {},
   "source": [
    "**We see an error**! \n",
    "\n",
    "This is because both `b` and `c` are writing to the same state key / channel in the same step. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "9048b041-6849-4f09-9811-6b7a80f67859",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding I'm A to []\n",
      "Adding I'm B to [\"I'm A\"]\n",
      "Adding I'm C to [\"I'm A\"]\n",
      "An error occurred: At key 'state': Can receive only one value per step. Use an Annotated key to handle multiple values.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/INVALID_CONCURRENT_GRAPH_UPDATE\n"
     ]
    }
   ],
   "source": [
    "from langgraph.errors import InvalidUpdateError\n",
    "try:\n",
    "    graph.invoke({\"state\": []})\n",
    "except InvalidUpdateError as e:\n",
    "    print(f\"An error occurred: {e}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64cc329d-59fa-4c26-adcf-9122a824955d",
   "metadata": {},
   "source": [
    "When using fan out, we need to be sure that we are using a reducer if steps are writing to the same the channel / key. \n",
    "\n",
    "As we touched on in Module 2, `operator.add` is a function from Python's built-in operator module.\n",
    "\n",
    "When `operator.add` is applied to lists, it performs list concatenation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "8f1292ac-510a-4801-b2a3-e2c6d2d9582a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import operator\n",
    "from typing import Annotated\n",
    "\n",
    "class State(TypedDict):\n",
    "    # The operator.add reducer fn makes this append-only\n",
    "    state: Annotated[list, operator.add]\n",
    "\n",
    "# Add nodes\n",
    "builder = StateGraph(State)\n",
    "\n",
    "# Initialize each node with node_secret \n",
    "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n",
    "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n",
    "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n",
    "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n",
    "\n",
    "# Flow\n",
    "builder.add_edge(START, \"a\")\n",
    "builder.add_edge(\"a\", \"b\")\n",
    "builder.add_edge(\"a\", \"c\")\n",
    "builder.add_edge(\"b\", \"d\")\n",
    "builder.add_edge(\"c\", \"d\")\n",
    "builder.add_edge(\"d\", END)\n",
    "graph = builder.compile()\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "ffbad231-fc1d-49b1-a9fc-ed9153fa3977",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding I'm A to []\n",
      "Adding I'm C to [\"I'm A\"]\n",
      "Adding I'm B to [\"I'm A\"]\n",
      "Adding I'm D to [\"I'm A\", \"I'm B\", \"I'm C\"]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'state': [\"I'm A\", \"I'm B\", \"I'm C\", \"I'm D\"]}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke({\"state\": []})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bdf5baa2-cecd-44b6-b0c4-d258340783f8",
   "metadata": {},
   "source": [
    "Now we see that we append to state for the updates made in parallel by `b` and `c`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed6fc7c7-198d-41be-867f-e77c93ba3217",
   "metadata": {},
   "source": [
    "## Waiting for nodes to finish\n",
    "\n",
    "Now, lets consider a case where one parallel path has more steps than the other one."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "f50b5d4f-dd39-4c22-b623-e0abc23f9144",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "builder = StateGraph(State)\n",
    "\n",
    "# Initialize each node with node_secret \n",
    "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n",
    "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n",
    "builder.add_node(\"b2\", ReturnNodeValue(\"I'm B2\"))\n",
    "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n",
    "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n",
    "\n",
    "# Flow\n",
    "builder.add_edge(START, \"a\")\n",
    "builder.add_edge(\"a\", \"b\")\n",
    "builder.add_edge(\"a\", \"c\")\n",
    "builder.add_edge(\"b\", \"b2\")\n",
    "builder.add_edge([\"b2\", \"c\"], \"d\")\n",
    "builder.add_edge(\"d\", END)\n",
    "graph = builder.compile()\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11640e6f-ac62-4ad4-89d9-7f6f9b56bf7a",
   "metadata": {},
   "source": [
    "In this case, `b`, `b2`, and `c` are all part of the same step.\n",
    "\n",
    "The graph will wait for all of these to be completed before proceeding to step `d`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "fafda930-e75b-410f-ba93-eb5fc0219303",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding I'm A to []\n",
      "Adding I'm B to [\"I'm A\"]\n",
      "Adding I'm C to [\"I'm A\"]\n",
      "Adding I'm B2 to [\"I'm A\", \"I'm B\", \"I'm C\"]\n",
      "Adding I'm D to [\"I'm A\", \"I'm B\", \"I'm C\", \"I'm B2\"]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'state': [\"I'm A\", \"I'm B\", \"I'm C\", \"I'm B2\", \"I'm D\"]}"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke({\"state\": []})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6610a2e3-b053-47e8-bf4e-0968dfaa0a5d",
   "metadata": {},
   "source": [
    "## Setting the order of state updates\n",
    "\n",
    "However, within each step we don't have specific control over the order of the state updates!\n",
    "\n",
    "In simple terms, it is a deterministic order determined by LangGraph based upon graph topology that **we do not control**. \n",
    "\n",
    "Above, we see that `c` is added before `b2`.\n",
    "\n",
    "However, we can use a custom reducer to customize this e.g., sort state updates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "24788e73-0950-432e-ad32-7987ea076529",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def sorting_reducer(left, right):\n",
    "    \"\"\" Combines and sorts the values in a list\"\"\"\n",
    "    if not isinstance(left, list):\n",
    "        left = [left]\n",
    "\n",
    "    if not isinstance(right, list):\n",
    "        right = [right]\n",
    "    \n",
    "    return sorted(left + right, reverse=False)\n",
    "\n",
    "class State(TypedDict):\n",
    "    # sorting_reducer will sort the values in state\n",
    "    state: Annotated[list, sorting_reducer]\n",
    "\n",
    "# Add nodes\n",
    "builder = StateGraph(State)\n",
    "\n",
    "# Initialize each node with node_secret \n",
    "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n",
    "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n",
    "builder.add_node(\"b2\", ReturnNodeValue(\"I'm B2\"))\n",
    "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n",
    "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n",
    "\n",
    "# Flow\n",
    "builder.add_edge(START, \"a\")\n",
    "builder.add_edge(\"a\", \"b\")\n",
    "builder.add_edge(\"a\", \"c\")\n",
    "builder.add_edge(\"b\", \"b2\")\n",
    "builder.add_edge([\"b2\", \"c\"], \"d\")\n",
    "builder.add_edge(\"d\", END)\n",
    "graph = builder.compile()\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "607dba2e-f9f0-4bc7-8ba6-684521a49bdc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding I'm A to []\n",
      "Adding I'm C to [\"I'm A\"]\n",
      "Adding I'm B to [\"I'm A\"]\n",
      "Adding I'm B2 to [\"I'm A\", \"I'm B\", \"I'm C\"]\n",
      "Adding I'm D to [\"I'm A\", \"I'm B\", \"I'm B2\", \"I'm C\"]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'state': [\"I'm A\", \"I'm B\", \"I'm B2\", \"I'm C\", \"I'm D\"]}"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke({\"state\": []})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb1714c0-e881-48e7-bcb8-a60016f0485e",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "Now, the reducer sorts the updated state values!\n",
    "\n",
    "The `sorting_reducer` example sorts all values globally. We can also: \n",
    "\n",
    "1. Write outputs to a separate field in the state during the parallel step\n",
    "2. Use a \"sink\" node after the parallel step to combine and order those outputs\n",
    "3. Clear the temporary field after combining\n",
    "\n",
    "<!-- See the [~docs~](https://langchain-ai.github.io/langgraph/how-tos/branching/#stable-sorting) [docs](https://docs.langchain.com/oss/python/langgraph/how-tos/graph-api#create-branches) for more details.-->\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34e0750b-e6af-40d9-835c-c664da5a2d3b",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "## Working with LLMs\n",
    "\n",
    "Now, lets add a realistic example! \n",
    "\n",
    "We want to gather context from two external sources (Wikipedia and Web-Search) and have an LLM answer a question."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "e1e9d03c-cb41-415c-862d-c9616d5a2d07",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "llm = ChatOpenAI(model=\"gpt-4o\", temperature=0) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "0f75cc78-d1a1-47a5-8648-bf5a79c883de",
   "metadata": {},
   "outputs": [],
   "source": [
    "class State(TypedDict):\n",
    "    question: str\n",
    "    answer: str\n",
    "    context: Annotated[list, operator.add]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e714ea8-095c-461a-98bc-ee782a84ef5c",
   "metadata": {},
   "source": [
    "You can try different web search tools. [Tavily](https://tavily.com/) is one nice option to consider, but ensure your `TAVILY_API_KEY` is set."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "c8bb519a-d08a-4ec7-8f0b-2ce6a9bf7342",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os, getpass\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "_set_env(\"TAVILY_API_KEY\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "bfb4f56c-3334-4927-8ed8-62fd384ee43e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage, SystemMessage\n",
    "\n",
    "from langchain_community.document_loaders import WikipediaLoader\n",
    "from langchain_tavily import TavilySearch  # updated since filming\n",
    "\n",
    "def search_web(state):\n",
    "    \n",
    "    \"\"\" Retrieve docs from web search \"\"\"\n",
    "\n",
    "    # Search\n",
    "    tavily_search = TavilySearch(max_results=3)\n",
    "    data = tavily_search.invoke({\"query\": state['question']})\n",
    "    search_docs = data.get(\"results\", data)\n",
    "\n",
    "     # Format\n",
    "    formatted_search_docs = \"\\n\\n---\\n\\n\".join(\n",
    "        [\n",
    "            f'<Document href=\"{doc[\"url\"]}\">\\n{doc[\"content\"]}\\n</Document>'\n",
    "            for doc in search_docs\n",
    "        ]\n",
    "    )\n",
    "\n",
    "    return {\"context\": [formatted_search_docs]} \n",
    "\n",
    "def search_wikipedia(state):\n",
    "    \n",
    "    \"\"\" Retrieve docs from wikipedia \"\"\"\n",
    "\n",
    "    # Search\n",
    "    search_docs = WikipediaLoader(query=state['question'], \n",
    "                                  load_max_docs=2).load()\n",
    "\n",
    "     # Format\n",
    "    formatted_search_docs = \"\\n\\n---\\n\\n\".join(\n",
    "        [\n",
    "            f'<Document source=\"{doc.metadata[\"source\"]}\" page=\"{doc.metadata.get(\"page\", \"\")}\">\\n{doc.page_content}\\n</Document>'\n",
    "            for doc in search_docs\n",
    "        ]\n",
    "    )\n",
    "\n",
    "    return {\"context\": [formatted_search_docs]} \n",
    "\n",
    "def generate_answer(state):\n",
    "    \n",
    "    \"\"\" Node to answer a question \"\"\"\n",
    "\n",
    "    # Get state\n",
    "    context = state[\"context\"]\n",
    "    question = state[\"question\"]\n",
    "\n",
    "    # Template\n",
    "    answer_template = \"\"\"Answer the question {question} using this context: {context}\"\"\"\n",
    "    answer_instructions = answer_template.format(question=question, \n",
    "                                                       context=context)    \n",
    "    \n",
    "    # Answer\n",
    "    answer = llm.invoke([SystemMessage(content=answer_instructions)]+[HumanMessage(content=f\"Answer the question.\")])\n",
    "      \n",
    "    # Append it to state\n",
    "    return {\"answer\": answer}\n",
    "\n",
    "# Add nodes\n",
    "builder = StateGraph(State)\n",
    "\n",
    "# Initialize each node with node_secret \n",
    "builder.add_node(\"search_web\",search_web)\n",
    "builder.add_node(\"search_wikipedia\", search_wikipedia)\n",
    "builder.add_node(\"generate_answer\", generate_answer)\n",
    "\n",
    "# Flow\n",
    "builder.add_edge(START, \"search_wikipedia\")\n",
    "builder.add_edge(START, \"search_web\")\n",
    "builder.add_edge(\"search_wikipedia\", \"generate_answer\")\n",
    "builder.add_edge(\"search_web\", \"generate_answer\")\n",
    "builder.add_edge(\"generate_answer\", END)\n",
    "graph = builder.compile()\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "fa544ca0-10af-491e-ad7a-477d004413eb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"Nvidia's Q2 2025 earnings were strong, as the company reported an earnings per share (EPS) of $1.04, beating the forecast of $1.01, resulting in a 2.97% surprise. Revenue also exceeded expectations, with the company reporting $46.74 billion in revenue and adjusted earnings per share of $1.05, both surpassing analyst estimates. This performance drove a stock uptick, indicating positive market reception. However, there were concerns about the company's operations in China, which remains a question mark.\""
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result = graph.invoke({\"question\": \"How were Nvidia's Q2 2025 earnings\"})\n",
    "result['answer'].content"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "3dbbecab-80eb-4f0c-b43a-45542fc0ae9c",
   "metadata": {},
   "source": [
    "## Using with LangGraph API\n",
    "\n",
    "**⚠️ Notice**\n",
    "\n",
    "Since filming these videos, we've updated Studio so that it can now be run locally and accessed through your browser. This is the preferred way to run Studio instead of using the Desktop App shown in the video. It is now called _LangSmith Studio_ instead of _LangGraph Studio_. Detailed setup instructions are available in the \"Getting Setup\" guide at the start of the course. You can find a description of Studio [here](https://docs.langchain.com/langsmith/studio), and specific details for local deployment [here](https://docs.langchain.com/langsmith/quick-start-studio#local-development-server).  \n",
    "To start the local development server, run the following command in your terminal in the `/studio` directory in this module:\n",
    "\n",
    "```\n",
    "langgraph dev\n",
    "```\n",
    "\n",
    "You should see the following output:\n",
    "```\n",
    "- 🚀 API: http://127.0.0.1:2024\n",
    "- 🎨 Studio UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024\n",
    "- 📚 API Docs: http://127.0.0.1:2024/docs\n",
    "```\n",
    "\n",
    "Open your browser and navigate to the **Studio UI** URL shown above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "4bc8ad8d-1365-4801-a8a5-b85cd4965119",
   "metadata": {},
   "outputs": [],
   "source": [
    "if 'google.colab' in str(get_ipython()):\n",
    "    raise Exception(\"Unfortunately LangSmith Studio is currently not supported on Google Colab\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "23919dc9-27d8-4d10-b91d-24acdf8c0fb9",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph_sdk import get_client\n",
    "client = get_client(url=\"http://127.0.0.1:2024\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "ff35e68f-4017-4f45-93cf-ddbb355a0bc1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Nvidia's Q2 2025 earnings were strong, with the company reporting an earnings per share (EPS) of $1.04, beating the forecast of $1.01. Revenue also exceeded expectations, coming in at $46.7 billion, surpassing the previous quarter's record of $44.1 billion and besting economist forecasts of $46.05 billion. This resulted in a positive surprise and drove a stock uptick.\n"
     ]
    }
   ],
   "source": [
    "thread = await client.threads.create()\n",
    "input_question = {\"question\": \"How were Nvidia Q2 2025 earnings?\"}\n",
    "async for event in client.runs.stream(thread[\"thread_id\"], \n",
    "                                      assistant_id=\"parallelization\", \n",
    "                                      input=input_question, \n",
    "                                      stream_mode=\"values\"):\n",
    "    # Check if answer has been added to state  \n",
    "    if event.data is not None:\n",
    "        answer = event.data.get('answer', None)\n",
    "        if answer:\n",
    "            print(answer['content'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54da0234-bddb-4c5b-9b93-b75b7c824d3b",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
